<?php
/**
 * Created by PhpStorm.
 * User: whwyy
 * Date: 2018/4/4 0004
 * Time: 13:05
 */

namespace Database;

use task\Cache;
use Yoc\Base\Config;

/**
 * Class Model
 *
 * @package Database
 */
class Model extends BaseModel
{

    /**
     * @var array
     * 参数绑定
     */
    public $attributes = [];

    /**
     * @return array|mixed|null|ActiveRecord
     * @throws \ReflectionException
     * @throws \Exception
     */
    public function one()
    {
        $this->before();
        $data = $this->command($this->adaptation())
            ->one();
        if (empty($data)) {
            return NULL;
        }
        return $this->populate($data);
    }

    /**
     * @throws \Exception
     */
    private function before()
    {
        $model = $this->getModel();
        if (method_exists($model, 'beforeSearch')) {
            $model->beforeSearch($model);
        }
    }

    /**
     * @throws \Exception
     */
    public function all()
    {
        $this->before();
        $data = $this->command($this->adaptation())->all();
        if (empty($data) || !is_array($data)) {
            return new Collection([]);
        }
        if (!$this->asArray && !empty($data)) {
            $_tmp = [];
            foreach ($data as $key => $val) {
                $_tmp[] = $this->populate($val);
            }
            $data = new Collection($_tmp);
        }
        return $data;
    }

    /**
     * @return array|mixed|null|ActiveRecord
     * @throws \Exception
     */
    public function queryRand()
    {
        $this->select('id')->orderBy('RAND()')
            ->limit(1);
        return $this->one();
    }

    /**
     * @param string $field
     * @param string $setKey
     *
     * @return array|null
     * @throws \Exception
     */
    public function column(string $field, $setKey = '')
    {
        return $this->all()->column($field, $setKey);
    }

    /**
     * @param $data
     *
     * @return ActiveRecord
     * @throws \ReflectionException
     * @throws \Exception
     */
    private function populate($data)
    {
        /** @var ActiveRecord $model */
        $model = clone $this->getModel();
        $model->setIsCreate(FALSE)->populate($data);
        $this->minute($model, $data);
        if (!empty($this->with)) {
            $this->relate($model);
        }
        return $model;
    }

    /**
     * @param ActiveRecord $model
     * @param $data
     * @throws
     */
    private function minute($model, $data)
    {
        foreach ($data as $key => $val) {
            $method = 'get' . ucfirst($key) . 'Attribute';
            if (!method_exists($model, $method)) {
                continue;
            }
            $model->setAttribute($key, $model->$method());
        }
    }

    /**
     * @return int
     * @throws \Exception
     */
    public function count()
    {
        $primary = $this->getPrimary();
        if (!empty($primary))  $this->select($primary);
        $data = $this->command($this->getQueryBuilder()->count($this))->one();
        $this->select = [];
        if ($data && is_array($data)) {
            return (int)array_shift($data);
        } else {
            return 0;
        }
    }

    /**
     * @param $filed
     *
     * @return null
     * @throws \Exception
     */
    public function value($filed)
    {
        if (!$this->getModel()->has($filed)) {
            return NULL;
        }
        $data = $this->asArray()->one();
        if (isset($data[$filed])) {
            return $data[$filed];
        } else {
            return NULL;
        }
    }

    /**
     * @return bool
     * @throws \Exception
     */
    public function exists()
    {
        $exists = (bool)$this->command($this->adaptation())->fetchColumn();
        return $exists;
    }

    /**
     * @return bool
     * @throws \Exception
     */
    public function deleteAll()
    {
        $sql = $this->getQueryBuilder()->delete($this);
        $exex = $this->command($sql)->delete();
        return $exex;
    }

    /**
     * @param $action
     *
     * @return string
     * @throws \Exception
     */
    private function adaptation()
    {
        return $this->getQueryBuilder()->builder($this);
    }

    /**
     * @param $sql
     * @param array $attr
     * @return Command
     * @throws \Exception
     */
    private function command($sql, $attr = [])
    {
        if (!empty($attr) && is_array($attr)) {
            $attr = array_merge($this->attributes, $attr);
        } else {
            $attr = $this->attributes;
        }
        return $this->getDb()->createCommand($sql, $attr);
    }

    /**
     * @return QueryBuilder
     * @throws \Exception
     */
    private function getQueryBuilder()
    {
        return $this->getDb()->getQueryBuilder();
    }
}